package cc.magicjson.summary.errors.connection.closed.monitor;

import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.Level;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import java.util.Collection;
import java.util.function.Supplier;

/**
 * 方法执行时间监控工具类
 * 支持：
 * 1. 自动获取调用方法名
 * 2. 自动识别/手动设置数据库表名(规范方法名才可以使用)
 * 3. 记录执行结果（包括集合大小）
 * 4. 兼容Java 8和Java 9+的调用栈获取方式
 */
@Component
@Slf4j
public class MethodStopWatch {

    // =============== Fields ===============
    private final String watchName;
    private final MethodNameResolver methodNameResolver;
    private final ThreadLocal<Object> lastTaskResult = new ThreadLocal<>();
    private final ThreadLocal<String> lastTableName = new ThreadLocal<>();

    // =============== Constructors ===============
    public MethodStopWatch() {
        this("DefaultWatch");
    }

    public MethodStopWatch(String watchName) {
        this.watchName = watchName;
        this.methodNameResolver = createMethodNameResolver();
    }

    // =============== Public Monitor Methods ===============
    /**
     * 监控有返回值的方法执行（自动获取方法名）
     * @param task 要执行的任务
     * @return 任务执行结果
     */
    public <T> T monitor(Supplier<T> task) {
        return monitor(null, Level.INFO, task);
    }

    /**
     * 监控有返回值的方法执行（支持自定义方法名）
     * @param customMethodName 自定义方法名
     * @param task 要执行的任务
     * @return 任务执行结果
     */
    public <T> T monitor(String customMethodName, Supplier<T> task) {
        return monitor(customMethodName, Level.INFO, task);
    }

    /**
     * 监控有返回值的方法执行（完整参数版本）
     * @param customMethodName 自定义方法名
     * @param logLevel 日志级别
     * @param task 要执行的任务
     * @return 任务执行结果
     */
    public <T> T monitor(String customMethodName, Level logLevel, Supplier<T> task) {
        String methodName = customMethodName != null ?
            customMethodName :
            methodNameResolver.getCallerMethodName();
        StopWatch taskStopWatch = new StopWatch(methodName);
        T result = null;
        try {
            taskStopWatch.start(methodName);
            result = task.get();
            lastTaskResult.set(result);
            return result;
        } finally {
            taskStopWatch.stop();
            logTaskInfo(methodName, taskStopWatch, logLevel);
            lastTaskResult.remove();
        }
    }

    /**
     * 监控无返回值的方法执行
     * @param task 要执行的任务
     */
    public void monitor(Runnable task) {
        monitor(null, task, Level.INFO);
    }

    /**
     * 监控无返回值的方法执行（支持自定义方法名和日志级别）
     */
    public void monitor(String customMethodName, Runnable task, Level level) {
        String methodName = customMethodName != null ?
            customMethodName :
            methodNameResolver.getCallerMethodName();

        StopWatch taskStopWatch = new StopWatch(methodName);
        try {
            taskStopWatch.start(methodName);
            task.run();
        } finally {
            taskStopWatch.stop();
            logTaskInfo(methodName, taskStopWatch, level);
        }
    }

    // =============== Table Name Support Methods ===============
    /**
     * 监控方法执行（支持显式指定表名）
     */
    public <T> T monitorWithTable(String tableName, Supplier<T> task) {
        return monitorWithTable(tableName, null, Level.INFO, task);
    }

    /**
     * 监控方法执行（支持显式指定表名和方法名）
     */
    public <T> T monitorWithTable(String tableName, String customMethodName, Supplier<T> task) {
        return monitorWithTable(tableName, customMethodName, Level.INFO, task);
    }

    /**
     * 监控方法执行（完整参数版本，支持表名）
     */
    public <T> T monitorWithTable(String tableName, String customMethodName, Level logLevel, Supplier<T> task) {
        String methodName = customMethodName != null ?
            customMethodName :
            methodNameResolver.getCallerMethodName();

        StopWatch taskStopWatch = new StopWatch(methodName);
        try {
            taskStopWatch.start(methodName);
            T result = task.get();
            lastTaskResult.set(result);
            lastTableName.set(tableName);
            return result;
        } finally {
            taskStopWatch.stop();
            logTaskInfo(methodName, taskStopWatch, logLevel);
            lastTaskResult.remove();
            lastTableName.remove();
        }
    }

    // =============== Private Helper Methods ===============
    private MethodNameResolver createMethodNameResolver() {
        try {
            Class.forName("java.lang.StackWalker");
            return new Java9MethodNameResolver();
        } catch (ClassNotFoundException e) {
            return new Java8MethodNameResolver();
        }
    }

    private void logTaskInfo(String taskName, StopWatch stopWatch, Level logLevel) {
        StopWatch.TaskInfo lastTaskInfo = stopWatch.getLastTaskInfo();
        Object result = lastTaskResult.get();

        StringBuilder messageBuilder = new StringBuilder()
            .append(String.format("[%s] Task-Method: %s", watchName, taskName));

        // 添加表名信息
        String tableName = lastTableName.get();
        if (tableName == null) {
            tableName = extractTableName(taskName);
        }
        if (tableName != null) {
            messageBuilder.append(String.format(", Table: %s", tableName));
        }

        // 添加结果大小信息
        if (result instanceof Collection) {
            messageBuilder.append(String.format(", Result size: %d", ((Collection<?>) result).size()));
        } else if (result instanceof Integer) {
            messageBuilder.append(String.format(", Result value: %d", result));
        }

        messageBuilder.append(String.format(", Time cost: %d ms", lastTaskInfo.getTimeMillis()));

        TransactionLogger.log(logLevel, messageBuilder.toString());
    }

    /**
     * 尝试从方法调用上下文中提取表名
     */
    private String extractTableName(String methodName) {
        // 1. 首先尝试从Mapper类名获取表名
        try {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            for (StackTraceElement element : stackTrace) {
                String className = element.getClassName();
                if (className.toLowerCase().contains("mapper")) {
                    String simpleClassName = className.substring(className.lastIndexOf('.') + 1);
                    return simpleClassName.replaceAll("Mapper$", "")
                                       .replaceAll("([a-z])([A-Z])", "$1_$2")
                                       .toLowerCase();
                }
            }
        } catch (Exception ignored) {
        }

        // 2. 尝试从方法名推断表名
        String lowerMethodName = methodName.toLowerCase();
        String[] commonPrefixes = {"insert", "update", "delete", "select", "get", "find", "query"};
        for (String prefix : commonPrefixes) {
            if (lowerMethodName.startsWith(prefix)) {
                String remaining = methodName.substring(prefix.length());
                if (remaining.startsWith("By")) {
                    remaining = remaining.substring(2);
                }
                return remaining.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
            }
        }

        return null;
    }

    // =============== Inner Interfaces and Classes ===============
    /**
     * 方法名解析器接口
     */
    private interface MethodNameResolver {
        String getCallerMethodName();

        default boolean isMethodStopWatchFrame(String className, String methodName) {
            return className.equals(MethodStopWatch.class.getName()) ||
                className.contains("lambda$") ||
                className.startsWith(MethodStopWatch.class.getName() + "$") ||
                className.equals("java.lang.Thread") ||
                className.contains("java.lang.reflect.") ||
                className.contains("$$Lambda$") ||
                methodName.equals("invoke") ||
                methodName.equals("get") ||
                methodName.equals("run");
        }
    }

    /**
     * Java 9+ 方法名解析器实现
     */
    private static class Java9MethodNameResolver implements MethodNameResolver {
        @Override
        public String getCallerMethodName() {
            return StackWalker.getInstance()
                .walk(frames -> frames
                    .dropWhile(frame -> isMethodStopWatchFrame(frame.getClassName(), frame.getMethodName()))
                    .findFirst()
                    .map(StackWalker.StackFrame::getMethodName)
                    .orElse("unknown"));
        }
    }

    /**
     * Java 8 方法名解析器实现
     */
    private static class Java8MethodNameResolver implements MethodNameResolver {
        @Override
        public String getCallerMethodName() {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            for (int i = 1; i < stackTrace.length; i++) {
                String className = stackTrace[i].getClassName();
                String methodName = stackTrace[i].getMethodName();
                if (!isMethodStopWatchFrame(className, methodName)) {
                    return stackTrace[i].getMethodName();
                }
            }
            return "unknown";
        }
    }
}
